unit namedindexselection;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
  ButtonPanel, database, fpjson;

type

  TNamedIndexSelectionType = (nistSwitch, nistVariable);

  { TNamedIndexSelectionForm }

  TNamedIndexSelectionForm = class(TForm)
    ResultButtonPanel: TButtonPanel;
    NameEdit: TEdit;
    IndexListBox: TListBox;
    NameGroupBox: TGroupBox;
    IndexSelectorGroupBox: TGroupBox;
    PageListBox: TListBox;
    SwitchSelectorPanel: TPanel;
    SetMaximumButton: TButton;
    PageGroupBox: TGroupBox;
    HeaderLabel: TLabel;
    PageSelectorPanel: TPanel;
    SetMaximumPanel: TPanel;
    procedure IndexListBoxSelectionChange(Sender: TObject; User: boolean);
    procedure NameEditChange(Sender: TObject);
    procedure ResultPanelButtonClick(Sender: TObject);
    procedure PageListBoxClick(Sender: TObject);
    procedure SetMaximumButtonClick(Sender: TObject);
  private
    Db: TDatabase;
    EditedArr: TJSONArray;
    SelectionType: TNamedIndexSelectionType;

    procedure FillPages(NumItems: Integer);
    { Page indexes are 0-based }
    procedure FillItems(PageIndex: Integer; NumItems: Integer);
    function FormatSwitchName(Id: Integer): String;

    { These functions don't change DB arrays}
    function ShowSwitchSelection(ADb: TDatabase): Boolean;
    function ShowVariableSelection(ADb: TDatabase): Boolean;
  public
    SelectedSwitch: Integer;
    SelectedVariable: Integer;

    { These functions change DB arrays }
    function SelectSwitch(OriginalId: Integer; ADb: TDatabase): Integer;
    function SelectVariable(OriginalId: Integer; ADb: TDatabase): Integer;

    function GetIndexPage(Id: Integer): Integer;
    function GetItemInPage(Id: Integer): Integer;
    function GetSelectedId: Integer;
  end;

resourcestring
  SwitchesHeader = 'Switches';
  VariablesHeader = 'Variables';

  SwitchSelectorHeading = 'Switch';
  VariableSelectorHeading = 'Variable';

const
  ITEMS_PER_PAGE = 20;

var
  NamedIndexSelectionForm: TNamedIndexSelectionForm;

implementation

uses
  Math, setmaximumformunit;

{$R *.lfm}

{ TNamedIndexSelectionForm }

procedure TNamedIndexSelectionForm.PageListBoxClick(Sender: TObject);
begin
  FillItems(PageListBox.ItemIndex, EditedArr.Count -1);
end;

procedure TNamedIndexSelectionForm.SetMaximumButtonClick(Sender: TObject);
var
  NewMaximum, I: Integer;
begin
  NewMaximum := SetMaximumForm.RequestValue(EditedArr.Count -1, 9999);
  if NewMaximum = EditedArr.Count -1 then
    Exit;

  if NewMaximum < EditedArr.Count -1 then
    for I := EditedArr.Count -1 downto NewMaximum -1 do
      EditedArr.Delete(I);

  if NewMaximum > EditedArr.Count -1 then
    for I := EditedArr.Count -1 to NewMaximum -1 do
      EditedArr.Add('');

  FillPages(EditedArr.Count -1);
  FillItems(PageListBox.ItemIndex, EditedArr.Count -1);
end;

procedure TNamedIndexSelectionForm.IndexListBoxSelectionChange(Sender: TObject;
  User: boolean);
begin
  NameEdit.Text := EditedArr.Strings[GetSelectedId];
end;

procedure TNamedIndexSelectionForm.NameEditChange(Sender: TObject);
var
  SelectedId: Integer;
begin
  SelectedId := GetSelectedId;
  if (SelectedId >= 1) and (SelectedId < EditedArr.Count) then
    EditedArr.Strings[SelectedId] := NameEdit.Text;
  IndexListBox.Items[IndexListBox.ItemIndex] := FormatSwitchName(SelectedId);
end;

procedure TNamedIndexSelectionForm.ResultPanelButtonClick(Sender: TObject);
begin
  ModalResult := (Sender as TPanelBitBtn).ModalResult;

  if ModalResult = mrOK then begin
    if SelectionType = nistSwitch then
      SelectedSwitch := GetSelectedId
    else if SelectionType = nistVariable then
      SelectedVariable := GetSelectedId
  end
end;

procedure TNamedIndexSelectionForm.FillPages(NumItems: Integer);
var
  I, NumPages: Integer;
  StartVal, EndVal: Integer;
  SavedItemIndex: Integer;
begin
  NumPages := Ceil((NumItems - 1) / ITEMS_PER_PAGE);

  PageListBox.Items.BeginUpdate;
  SavedItemIndex := PageListBox.ItemIndex;
  PageListBox.ClearSelection;
  PageListBox.Items.Clear;
  for I := 0 to NumPages - 1 do begin
    StartVal := I * ITEMS_PER_PAGE + 1;

    if I <> NumPages - 1 then
      EndVal := (I+1) * ITEMS_PER_PAGE
    else
      EndVal := NumItems;

    PageListBox.Items.Add('%0.4d - %0.4d'.Format([StartVal, EndVal]));
  end;
  PageListBox.Items.EndUpdate;
  IndexListBox.ItemIndex := Min(SavedItemIndex, PageListBox.Count - 1);
end;

procedure TNamedIndexSelectionForm.FillItems(PageIndex: Integer;
  NumItems: Integer);
var
  I: Integer;
  StartItem, EndItem: Integer;
  SavedItemIndex: Integer;
begin
  StartItem := PageIndex * ITEMS_PER_PAGE + 1;
  EndItem := Min(NumItems, (PageIndex + 1) * ITEMS_PER_PAGE);

  IndexListBox.Items.BeginUpdate;
  SavedItemIndex := IndexListBox.ItemIndex;
  IndexListBox.ClearSelection;
  IndexListBox.Items.Clear;
  for I := StartItem to EndItem do begin
    if SelectionType = nistSwitch then
      IndexListBox.Items.Add(FormatSwitchName(I))
    else
      IndexListBox.Items.Add(FormatSwitchName(I))
  end;
  IndexListBox.Items.EndUpdate;
  IndexListBox.ItemIndex := Min(SavedItemIndex, IndexListBox.Count - 1);
end;

function TNamedIndexSelectionForm.FormatSwitchName(Id: Integer): String;
begin
  FormatSwitchName := '%0.4d %s'.Format([Id, EditedArr.Strings[Id]]);
end;

function TNamedIndexSelectionForm.ShowSwitchSelection(ADb: TDatabase): Boolean;
begin
  Db := ADb;
  SelectionType := nistSwitch;
  HeaderLabel.Caption := SwitchesHeader;
  IndexSelectorGroupBox.Caption := SwitchSelectorHeading;
  EditedArr := Db.System.Arrays['switches'].Clone as TJSONArray;
  Caption := SwitchesHeader;

  FillPages(EditedArr.Count -1);
  PageListBox.ItemIndex := GetIndexPage(SelectedSwitch);
  FillItems(PageListBox.ItemIndex, EditedArr.Count -1);
  IndexListBox.ItemIndex := GetItemInPage(SelectedSwitch);

  ShowModal;

  ShowSwitchSelection := ModalResult = mrOK;
  if ShowSwitchSelection then
    SelectedSwitch := GetSelectedId;
end;

function TNamedIndexSelectionForm.ShowVariableSelection(ADb: TDatabase
  ): Boolean;
begin
  Db := ADb;
  SelectionType := nistVariable;
  HeaderLabel.Caption := VariablesHeader;
  IndexSelectorGroupBox.Caption := VariableSelectorHeading;
  EditedArr := Db.System.Arrays['variables'].Clone as TJSONArray;
  Caption := VariablesHeader;

  FillPages(EditedArr.Count -1);
  PageListBox.ItemIndex := GetIndexPage(SelectedVariable);
  FillItems(PageListBox.ItemIndex, EditedArr.Count -1);
  IndexListBox.ItemIndex := GetItemInPage(SelectedVariable);

  ShowModal;

  ShowVariableSelection := ModalResult = mrOK;
  if ShowVariableSelection then
    SelectedVariable := GetSelectedId;
end;

function TNamedIndexSelectionForm.SelectSwitch(OriginalId: Integer;
  ADb: TDatabase): Integer;
begin
  if ShowSwitchSelection(ADb) then begin
    SelectSwitch := SelectedSwitch;
    Db.System.Arrays['switches'] := EditedArr;
  end else begin
    SelectSwitch := OriginalId;
    EditedArr.Free;
    EditedArr := nil
  end;
end;

function TNamedIndexSelectionForm.SelectVariable(OriginalId: Integer;
  ADb: TDatabase): Integer;
begin
  if ShowVariableSelection(ADb) then begin
    SelectVariable := SelectedVariable;
    Db.System.Arrays['variables'] := EditedArr;
  end else begin
    SelectVariable := OriginalId;
    EditedArr.Free;
    EditedArr := nil
  end;
end;

function TNamedIndexSelectionForm.GetIndexPage(Id: Integer): Integer;
var
  PageIndex: Integer;
begin
  PageIndex := ((Id - 1) div ITEMS_PER_PAGE);
  GetIndexPage := Max(0, PageIndex);
end;

function TNamedIndexSelectionForm.GetItemInPage(Id: Integer): Integer;
begin
  GetItemInPage := (Id - 1) mod 20;
end;

function TNamedIndexSelectionForm.GetSelectedId: Integer;
var
  PageOffset: Integer;
begin
  PageOffset := PageListBox.ItemIndex * ITEMS_PER_PAGE + 1;
  GetSelectedId := Max(1, PageOffset + IndexListBox.ItemIndex);
end;

end.

